package cmm;
import cmm.Seman.FourCode;
import cmm.Seman.Symbol;
import cmm.Seman.SymbolTable;
import cmm.Seman.Value;
import gui.GUI;


import javax.swing.*;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.logging.ConsoleHandler;

/**
 * Created by Iray on 2017/12/1.
 */
public class Executor {
    private static int point;
    private static int mLevel;
    private static SymbolTable symbolTable;

    public StringBuffer getBuffer() {
        return buffer;
    }

    private StringBuffer buffer =new StringBuffer();
    public Executor(LinkedList<FourCode> codes) throws Exception {
        point = 0;
        symbolTable = SymbolTable.getSymbolTable();
        while (point < codes.size() && point > -1) {
            execute(codes.get(point));
        }
        symbolTable.deleteTable();
    }

    private void execute(FourCode code) throws Exception {

        switch (code.getFirst()) {
            case FourCode.JUMP:
            {
                if (code.getSecond() == null || symbolTable.findValue(code.getSecond()).getType() == Symbol.FALSE) {//需要跳转
                    point = getValue(code.getForth()).getiValue();
                    return;
                }
                break;
            }

            case FourCode.READ:
                System.out.println("请为" + code.getForth()+ "赋值");
                Scanner scanner = new Scanner(System.in);
                String input = scanner.next();
                int type;
                if(code.getForth().contains("[")) {
                    type = symbolTable.getType(code.getForth().substring(0, code.getForth().indexOf("[")));
                } else {
                    type = symbolTable.getType(code.getForth());
                }

                switch (type) {
                    case Symbol.INT:
                    case Symbol.INTL: {
                        Value value = analysisInput(input);
                        assignValue(code.getForth(), value);

                        break;
                    }
                    case Symbol.REAL:
                    case Symbol.REALL: {
                        Value value = analysisInput(input);
                        assignValue(code.getForth(), value);
                        break;
                    }
                    default:
                        break;
                }

            case FourCode.WRITE:
            {
                String element = code.getForth();
                if (element.contains("[")) {
                    String indexstr = element.substring(element.indexOf("[") + 1, element.length() - 1);
                    int idx = getInt(indexstr);
                    String id = element.substring(0, element.indexOf("["));
                    System.out.println(symbolTable.findValue(id).toString(idx));
                    buffer.append(symbolTable.findValue(id).toString(idx)+"\n");
                }else {
                    if (element.matches("^-*[0-9]+$") || element.matches("-*[0-9]+(\\.[0-9]+)*")) {
                        System.out.println(element);
                        buffer.append(element+"\n");
                        break;
                    }
                    System.out.println(symbolTable.findValue(element).toString(0));
                    buffer.append(symbolTable.findValue(element).toString(0)+"\n");
                }

                break;
            }
            case FourCode.INT:
            {
                if (code.getThird() != null) {
                    Symbol symbol = new Symbol(code.getForth(), Symbol.INTL, mLevel);
                    symbol.getValue().setIntArray(getInt(code.getThird()));
                    symbolTable.registe(symbol);
                } else {
                    Value value = new Value(Symbol.INT);
                    value.setiValue(0);

                    if (code.getSecond() != null) {
                        value.setiValue(getInt(code.getSecond()));
                    }
                    Symbol symbol = new Symbol(code.getForth(), Symbol.INT, mLevel, value);
                    symbolTable.registe(symbol);
                }
                break;
            }

            case FourCode.REAL:
            {
                if (code.getThird() != null) {
                    Symbol symbol = new Symbol(code.getForth(), Symbol.REALL, mLevel);
                    symbol.getValue().setRealArray(getInt(code.getThird()));
                    symbolTable.registe(symbol);
                } else {
                    Value value = new Value(Symbol.REAL);
                    value.setdValue(0.0);
                    if (code.getSecond() != null) {
                        value.setdValue(getReal(code.getSecond()));
                    }
                    Symbol symbol = new Symbol(code.getForth(), Symbol.REAL, mLevel, value);
                    symbolTable.registe(symbol);
                }
                break;
            }
            case FourCode.ASSIGN:
            {
                Value value = getValue(code.getSecond());
                assignValue(code.getForth(), value);
                break;
            }
            case FourCode.PLUS:
                assignValue(code.getForth(), getValue(code.getSecond()).ADD(getValue(code.getThird())));
                break;
            case FourCode.MINUS:
                assignValue(code.getForth(), getValue(code.getSecond()).SUB(getValue(code.getThird())));
                break;
            case FourCode.MUL:
                assignValue(code.getForth(), getValue(code.getSecond()).MUL(getValue(code.getThird())));
                break;
            case FourCode.DIV:
                assignValue(code.getForth(), getValue(code.getSecond()).DIV(getValue(code.getThird())));
                break;
            case FourCode.GT:
                assignValue(code.getForth(), getValue(code.getSecond()).GT(getValue(code.getThird())));
                break;
            case FourCode.LT:
                assignValue(code.getForth(), getValue(code.getSecond()).LT(getValue(code.getThird())));
                break;
            case FourCode.GET:
                assignValue(code.getForth(), getValue(code.getSecond()).GET(getValue(code.getThird())));
                break;
            case FourCode.LET:
                assignValue(code.getForth(), getValue(code.getSecond()).LET(getValue(code.getThird())));
                break;
            case FourCode.EQ:
                assignValue(code.getForth(), getValue(code.getSecond()).EQ(getValue(code.getThird())));
                break;
            case FourCode.NEQ:
                assignValue(code.getForth(), getValue(code.getSecond()).NEQ(getValue(code.getThird())));
                break;
            case FourCode.IN:
                mLevel++;
                break;
            case FourCode.OUT:
                symbolTable.unregiste(mLevel);
                mLevel--;
                break;

            default:
                throw new Exception("未知四元式");
        }

        point++;
    }


    private void assignValue(String id, Value value) throws Exception {
        if(id.contains("[")) {
            String idxS = id.substring(id.indexOf("[")+1, id.length()-1);
            String name = id.substring(0, id.indexOf("["));
            int idx = 0;
            if (!idxS.matches("-*[0-9]+"))
                idx = Integer.parseInt(symbolTable.findValue(idxS).toString(0));
            else
                idx = Integer.parseInt(idxS);
            if(symbolTable.getType(name) == Symbol.INTL) {
                symbolTable.setValue(name, idx, Integer.parseInt(value.toString(0)));
            } else if( symbolTable.getType(name) == Symbol.REALL) {
                symbolTable.setValue(name, idx, Double.parseDouble(value.toString(0)));
            }

        }else {
            if(!symbolTable.setValue(id, value)) {
                int type = value.getType();
                if(id.contains("*temp")) {
                    Symbol symbol = new Symbol(id, type, mLevel, value);
                    symbolTable.registe(symbol);
                }
            }
        }

    }

    private Value getValue(String id) throws Exception {
        if (id.matches("-*\\d*\\.\\d*")) {
            Value value = new Value(Symbol.REAL);
            value.setdValue(Double.parseDouble(id));
            return value;
        }else if (id.matches("^-*[0-9]+$")) {
            Value value = new Value(Symbol.INT);
            value.setiValue(Integer.parseInt(id));
            return value;
        }else if(!id.contains("[")) {
            return symbolTable.findValue(id);
        }else if(id.contains("[")) {
            Value value =  symbolTable.findValue(id.substring(0, id.indexOf("[")));
            String va = value.toString(getInt(id.substring(id.indexOf("[") +1, id.length()-1)));
            int type;
            Value retrn = null;
            switch (value.getType()) {
                case Symbol.INTL:
                    type = Symbol.INT;
                    retrn = new Value(type);
                    retrn.setiValue(Integer.parseInt(va));
                    break;
                case Symbol.REALL:
                    type = Symbol.REAL;
                    retrn = new Value(type);
                    retrn.setdValue(Double.parseDouble(va));
                    break;
            }

            return retrn;
        }
        throw new Exception(id + "不存在对应的值");
    }

    private int getInt(String s) throws Exception {
        if(!s.matches("^-*[0-9]+$")) {
            return symbolTable.findValue(s).getiValue();
        } else {
            return Integer.parseInt(s);

        }


    }

    private double getReal(String s) throws Exception {

        if(!s.matches("-*[0-9]+(\\.[0-9]+)*")) {
            return symbolTable.findValue(s).getdValue();
        } else {
            return Double.parseDouble(s);

        }

    }

    private Value analysisInput(String input) throws Exception {
        Value value;

        //先匹配浮点数，这里有点隐患，浮点数可以顺利匹配整形
        if(input.matches("-*[0-9]+(\\.[0-9]+)*")) {
            value = new Value(Symbol.REAL);
            value.setdValue(Double.parseDouble(input));
            return value;
        }else if(input.matches("^-*[0-9]+$")) {
            value = new Value(Symbol.INT);
            value.setiValue(Integer.parseInt(input));
            return value;
        }
        throw new Exception("input 输入有误");

    }

}
